/*
 * @Author: czy
 * @Date: 2021-07-09 08:43:27
 * @LastEditTime: 2021-07-14 22:29:12
 */
#include "stdbool.h"
#include "string.h"
#include "stdarg.h"

#include "sys/container.h"
#include "sys/system.h"

static int order(void *zset, int score1, int score2)
{
    Zset *z = (Zset *)zset;
    if (z->reverse == 0)
    {
        return score1 <= score2;
    }
    else
    {
        return score1 >= score2;
    }
}

static void delete (void *zset, ZNode *node)
{
    Zset *z = (Zset *)zset;
    if (node->previous != NULL)
    {
        node->previous->next = node->next;
    }
    else
    {
        z->head = node->next;
        z->head->previous = NULL;
    }

    if (node->next != NULL)
    {
        node->next->previous = node->previous;
    }
    else
    {
        z->tail = node->previous;
        node->previous->next = NULL;
    }
    z->current = z->tail;
}

static void insert(void *zset, ZNode *node)
{
    Zset *z = (Zset *)zset;
    
    if (z->length == 0)
    {
        node->next = NULL;
        node->previous = NULL;
        z->head = node;
        z->tail = node;
    }
    else
    {
        ZNode *temp = z->head;
        int flag = false;
        while (temp != NULL)
        {
            if (!order(zset, temp->score, node->score))
            {
                node->next = temp;
                node->previous = temp->previous;
                temp->previous->next = node;
                if (node->previous != NULL)
                {
                    node->previous->next = node;
                }
                else
                {
                    z->head = node;
                }
                flag = true;
                break;
            }
            temp = temp->next;
        }
        if (flag == false)
        {
            z->tail->next = node;
            node->previous = z->tail;
            node->next = NULL;
            z->tail = node;
        }
    }
    z->current = node;
}

static void search(void *zset, int index, ZNode **node)
{
    Zset *z = (Zset *)zset;
    ZNode *temp;
    if (index >= 0)
    {
        temp = z->head;
        temp->previous = NULL;
        for (int i = 0; i < index; i++)
        {
            temp = temp->next;
        }
    }
    else
    {
        temp = z->tail;
        for (int i = 0; i < -index - 1; i++)
        {
            temp = temp->previous;
        }
    }
    z->current = temp;
    *node = temp;
}

static void match(void *zset, int score, ZNode **node)
{
    Zset *z = (Zset *)zset;
    if (z->length == 0)
    {
        *node = NULL;
        return;
    }
    ZNode *temp = z->head;
    while (temp != NULL)
    {
        if (score == temp->score)
        {
            *node = temp;
            z->current = temp;
            return;
        }
        else if ((score < temp->score && !z->reverse) || (score > temp->score && z->reverse))
        {
            *node = NULL;
            return;
        }
        temp = temp->next;
    }
    *node = NULL;
}

int zset_add(void *zset, int score, ...)
{
    Zset *z = (Zset *)zset;
    va_list args;
    va_start(args, score);
    ZNode *node = (ZNode *)kmalloc(sizeof(ZNode) + z->size);
    memcpy(node->data, va_arg_by_size(args, z->size), z->size);
    va_end(args);
    node->score = score;
    insert(zset, node);
    z->length++;
    return true;
}

void *zset_pop(void *zset, int index, void *data)
{
    Zset *z = (Zset *)zset;
    if (index < -z->length || index >= z->length)
    {
        return NULL;
    }
    ZNode *temp;
    search(zset, index, &temp);
    z->length--;
    memcpy(data, temp->data, z->size);
    delete (zset, temp);
    z->head->previous = NULL;
    kfree(temp);
    return data;
}

void *zset_get(void *zset, int index)
{
    Zset *z = (Zset *)zset;
    if (index < -(z->length) || index >= z->length)
    {
        return NULL;
    }
    ZNode *temp;
    search(zset, index, &temp);
    return temp->data;
}

void *zset_next(void *zset)
{
    Zset *z = (Zset *)zset;
    void *data = NULL;
    if (z->current != NULL)
    {
        data = (void *)z->current->data;
        z->current = z->current->next;
    }
    return data;
}

void *zset_previous(void *zset)
{
    Zset *z = (Zset *)zset;
    void *data = NULL;
    if (z->current != NULL)
    {
        data = z->current->data;
        z->current = z->current->previous;
    }
    return data;
}

void *zset_find(void *zset, int score)
{
    Zset *z = (Zset *)zset;
    ZNode *temp;
    match(zset, score, &temp);
    if (temp != NULL)
    {
        return temp->data;
    }
    return NULL;
}

void zset_set(void *zset, int index, int score)
{
    Zset *z = (Zset *)zset;
    if (index < -z->length || index >= z->length)
    {
        return;
    }
    ZNode *node;
    search(zset, index, &node);
    delete (zset, node);
    node->score = score;
    insert(zset, node);
}

void zset_clear(void *zset)
{
    Zset *z = (Zset *)zset;
    ZNode *temp = z->head;
    ZNode *next;
    while (temp != NULL)
    {
        next = temp->next;
        kfree(temp);
        temp = next;
    }
    z->length = 0;
    z->head = NULL;
    z->tail = NULL;
    z->current = NULL;
}

void zset_delete(void *zset, int index)
{
    Zset *z = (Zset *)zset;
    ZNode *node;
    search(zset, index, &node);
    delete (zset, node);
}

void *zset_remove(void *zset, int score, void *data)
{
    Zset *z = (Zset *)zset;
    ZNode *temp;
    match(zset, score, &temp);
    if (temp == NULL)
    {
        return NULL;
    }
    delete (zset, temp);
    z->length--;
    memcpy(data, temp->data, z->size);
    kfree(temp);
    return data;
}